#ifndef LEVEL1_H_
#define LEVEL1_H_

#include <sys/socket.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <netdb.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <arpa/inet.h>
#include <sys/time.h>

#endif

#define TAM_INICIAL 125
#define TAM_FINAL TAM_INICIAL*8


/* Return 1 if the difference is negative, otherwise 0.  */
int timeval_subtract(struct timeval *result, struct timeval *t2, struct timeval *t1)
{
    long int diff = (t2->tv_usec + 1000000 * t2->tv_sec) - (t1->tv_usec + 1000000 * t1->tv_sec);
    result->tv_sec = diff / 1000000;
    result->tv_usec = diff % 1000000;

    return (diff<0);
}

void timeval_sum(struct timeval *result, struct timeval *t2, struct timeval *t1)
{
    long int sum = (t2->tv_usec + 1000000 * t2->tv_sec) + (t1->tv_usec + 1000000 * t1->tv_sec);
    result->tv_sec = sum / 1000000;
    result->tv_usec = sum % 1000000;
}

void timeval_elapsed(struct timeval elapsed)
{
    unsigned long int aux;
    aux = elapsed.tv_usec + elapsed.tv_sec*1000000;
	printf("%ld\n", aux);
}

int getTime(struct timeval *t)
{
    return gettimeofday(t, NULL);
}


//código do quicksort, retirado de: http://pt.wikipedia.org/wiki/Quicksort
void swap(int* a, int* b)
{
    int tmp;
    tmp = *a;
    *a = *b;
    *b = tmp;
}

int partition(int vec[], int left, int right)
{
    int i, j;

    i = left;
    for (j = left + 1; j <= right; ++j)
    {
        if (vec[j] < vec[left])
        {
            ++i;
            swap(&vec[i], &vec[j]);
        }
    }
    swap(&vec[left], &vec[i]);

    return i;
}

void quickSort(int vec[], int left, int right)
{
    int r;

    if (right > left)
    {
        r = partition(vec, left, right);
        quickSort(vec, left, r - 1);
        quickSort(vec, r + 1, right);
    }
}

void enviarVetor(char server_name[], int vetorInicial[], int proxima_porta, struct timeval *start, struct timeval *end)
{
	int sock, bytes_recv, i;
	struct hostent *host;
	struct sockaddr_in server_addr;

	host = gethostbyname(server_name);


	if ((sock = socket(AF_INET, SOCK_STREAM, 0)) == -1)
	{
		perror("Erro de Socket");
		exit(1);
	}

	server_addr.sin_family = AF_INET;
	server_addr.sin_port = htons(proxima_porta);
	server_addr.sin_addr = *((struct in_addr *)host->h_addr);
	bzero(&(server_addr.sin_zero),8);

	if (connect(sock, (struct sockaddr *)&server_addr, sizeof(struct sockaddr)) == -1)
	{
		perror("Erro de conexao");
		exit(1);
	}

	if(getTime(start))
	{
		printf("Nao foi possivel de guardar o tempo de inicio\n");
		exit(1);
	}

	send(sock,vetorInicial,TAM_INICIAL * sizeof(int), 0);

	if(getTime(end))
	{
		printf("Nao foi possivel guardar o tempo de fim\n");
		exit(1);
	}

	close(sock);
}

void receberVetorFinal( char server_name[],int vetorFinal[], int porta_anterior, struct timeval *start, struct timeval *end)
{
	int sock, bytes_recv, i;
	struct hostent *host;
	struct sockaddr_in server_addr;

	host = gethostbyname(server_name);

	if ((sock = socket(AF_INET, SOCK_STREAM, 0)) == -1)
	{
		perror("Erro de Socket");
		exit(1);
	}

	server_addr.sin_family = AF_INET;
	server_addr.sin_port = htons(porta_anterior);
	server_addr.sin_addr = *((struct in_addr *)host->h_addr);
	bzero(&(server_addr.sin_zero),8);

	if (connect(sock, (struct sockaddr *)&server_addr, sizeof(struct sockaddr)) == -1)
	{
	    printf("Erro de conexão: PORTA: %d",porta_anterior );
		exit(1);
	}
	printf("\n%d\n",sock);
	if(getTime(start))
	{
		printf("Nao foi possivel de guardar o tempo de inicio\n");
		exit(1);
	}
	recv(sock, vetorFinal, TAM_FINAL * sizeof(int), MSG_WAITALL);
	if(getTime(end))
	{
		printf("Nao foi possivel guardar o tempo de fim\n");
		exit(1);
	}
	close(sock);
}

//Level1node <nome do servidor de nível 2> <porta proximo nivel> <porta nivel anterior> <nome do server principal>
int main(int argc, char *argv[ ])
{
	int vetorInicial[TAM_INICIAL], vetorFinal[TAM_FINAL], i;
	struct timeval elapsed, msg_start_send, msg_end_send, msg_elapsed_send, msg_start_recieve,
        msg_end_recieve, msg_elapsed_recieve, sort_start, sort_end, sort_elapsed;


	if(argc != 5)
	{
		printf("\n Digite o nome ou endereco IP do servidor ao inicializar o programa, porta de entrada e porta de saida\n");
		return 0;
	}

	srand( (unsigned)time(NULL) );

	for(i=0; i<TAM_INICIAL; i++)
		vetorInicial[i] = rand()%10001;

	getTime(&sort_start);
   	quickSort(vetorInicial,0,TAM_INICIAL);
	getTime(&sort_end);

    	printf("Enviando...\n");
	enviarVetor(argv[1], vetorInicial, atoi(argv[2]), &msg_start_send, &msg_end_send);
    	printf("Enviado...\n");
	receberVetorFinal(argv[4],vetorFinal, atoi(argv[3]), &msg_start_recieve, &msg_end_recieve);


	//Calcula o tempo de envio da mensagem
	timeval_subtract(&msg_elapsed_send, &msg_end_send, &msg_start_send);

	timeval_subtract(&sort_elapsed, &sort_end, &sort_start);

	timeval_subtract(&msg_elapsed_recieve, &msg_end_recieve, &msg_start_recieve);

	//Calcula a variacao do tempo
	timeval_sum(&elapsed, &msg_elapsed_send, &sort_elapsed);
	timeval_sum(&elapsed, &elapsed, &msg_elapsed_recieve);

	printf("VETOR INICIAL:\n\n");
	for(i = 0; i<TAM_INICIAL; i++)
	printf(" %d ", vetorInicial[i]);
	printf("\n\nVETOR FINAL: \n\n");
	for(i = 0; i<TAM_FINAL; i++)
	printf(" %d ", vetorFinal[i]);

	//Imprime o tempo gasto
	printf("\n\ntempo gasto:\n");
	timeval_elapsed(elapsed);

	printf("\n\nMensagem de envio gastou:\n");
	//Imprime o tempo de socket
	timeval_elapsed(msg_elapsed_send);

	printf("\n\nsorting gastou:\n");
	timeval_elapsed(sort_elapsed);

	printf("\n\nMensagem de recebimento gastou:\n");
	//Imprime o tempo de socket
	timeval_elapsed(msg_elapsed_recieve);

	return 0;

}
